package com.dy.yunying.biz.utils.mongo;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.Assert;

import com.mongodb.client.result.UpdateResult;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MongoDBUtil {
	
	private static MongoTemplate mongoTemplate = ApplicationContextUtil.getBean(MongoTemplate.class);
	
	/**
     * 保存对象List到指定集合中
     * <p>
     * 也可以在实体类上使用@Document(collection=“集合名称”)指定集合名称，未指定则默认实体类的类名为集合名称
     * @param entiys
     */
    public static void saveAll(String collName, List<?> entiys) {
        for (Object entiy : entiys) {
            saveData(collName, entiy);
        }
    }
    
    /**
     * 保存单个对象到指定集合中
     *
     * @param collName 集合名称
     * @param entiy    实体名称
     */
    public static <T> T saveOne(String collName, T entiy) {
        return saveData(collName, entiy);
    }


    /**
     * 根据id倒序查询 集合中的数据
     *
     * @param entiy     数据实体
     * @param collName  集合名称
     * @param direction 倒序/正序 Direction.DESC/ASC
     * @param
     */
    public static <T> List<T> findSortById(Class<T> entiy, String collName, Sort.Direction direction,int limit) {
        Query query = new Query().with(Sort.by(direction, "id")).skip(0).limit(limit);
        return MongoDBUtil.mongoTemplate.find(query, entiy, collName);
    }
    
    /**
     * 查询返回指定字段
     *
     * @param fields   需要返回的指定字段  eg: fields.add("runTime");
     * @param clazz    数据实体类class
     * @param collName 集合名称
     * @param map      Map<查询条件key,查询条件value>  eg: map.put("describe", "查询用户信息");
     * @param returnId 返回字段的时候id默认为返回，不返回id则field设置
     * @return
     */
    public static <T> List<T> findDesignField(List<String> fields, Map<String, Object> map, Class<T> clazz, String collName, boolean returnId) {
        Criteria criteria = null;
        for (String key : map.keySet()) {
            criteria = Criteria.where(key).is(map.get(key));
        }
        Query query = new Query(criteria);
        for (String field : fields) {
            query.fields().include(field);
        }
        if (!returnId) {
            query.fields().exclude("id");
        }
        return mongoTemplate.find(query, clazz, collName);
    }
    
    /**
     * 查询指定集合中的所有数据
     *
     * @param entiy    数据实体类
     * @param collName 集合名称
     */
    public static <T> List<T> findAll(Class<T> entiy, String collName) {
        return mongoTemplate.findAll(entiy, collName);
    }
    
    /**
     * 模糊查询 根据 key 可以到 collName 中进行模糊查询 并排序
     *
     * @param param     匹配的参数名称
     * @param key       模糊搜索关键字
     * @param collName  集合名称
     * @param sortField 排序字段
     * @param direction Direction.desc /asc 倒序/正序
     * @param clazz 返回对象
     * @return java.lang.Object
     **/
    public static <T> List<T> findLikeByParam(String param, String key, String collName, String sortField, Sort.Direction direction,Class<T> clazz) {
        Pattern pattern = Pattern.compile("^.*" + key + ".*$", Pattern.CASE_INSENSITIVE);
        Query query = new Query(Criteria.where(param).regex(pattern)).with(Sort.by(direction, sortField));
        return mongoTemplate.find(query, clazz, collName);
    }
    
    
    /**
     * @param map 查询条件
     * @param collName 集合名称
     * @param clazz 返回对象
     * @return
     */
    public static <T> List<T> findAllByParam(Map<String, Object> map, String collName, Class<T> clazz) {
    	log.info("findOneByParam  --->map:{} ,map:{}",collName,map);
    	Criteria criteria = getCriteria(new Criteria(), map);
    	Query query = Query.query(criteria);
        return mongoTemplate.find(query, clazz, collName);
    }
    
    /**
     * 精确查询一条 匹配查询
     * @param map 查询条件
     * @param collName 集合名称
     * @param clazz 返回对象
     * @return
     */
    public static <T> T findOneByParam(Map<String, Object> map, String collName, Class<T> clazz) {
    	log.info("findOneByParam  --->map:{} ,map:{}",collName,map);
    	Criteria criteria = getCriteria(new Criteria(), map);
        Query query = Query.query(criteria);
        return mongoTemplate.findOne(query, clazz, collName);
    }
    
    /**
     * 根据指定key 和value到指定collName集合中删除数据
     *
     * @param key
     * @param value
     * @param collName
     */
    public static void removeAllByParam(Map<String, Object> map, String collName) {
        if(Objects.isNull(map) || map.isEmpty()){
        	Assert.notNull(map, "删除条件不能为null");
        }
        Criteria criteria = getCriteria(new Criteria(), map);
        Query query = Query.query(criteria);
        mongoTemplate.remove(query, collName);
    }
    
    /**
     * 指定集合 修改数据，且修改所找到的所有数据
     * @param condition  修改条件
     * @param updateMap  Map<修改内容 key数组,修改内容 value数组>
     * @param collName 集合名
     * @param type 修改操作类型  1:修改第一条数据  0：修改所有匹配得数据
     * 将属性更新为空时,自动跳过
     */
    public static Long updateMulti(Map<String,Object> condition, Map<String, Object> updateMap,
                                   String collName, Integer type) {
        
        if(Objects.isNull(condition) || condition.isEmpty()){
        	Assert.notNull(null, "mongdb修改,查询条件key不能为null");
        }
        
        if(Objects.isNull(updateMap) || updateMap.isEmpty()){
        	//修改属性没有,直接退出
        	return 0L;
        }
        //更新时间
        updateMap.put("update_time", new Date());
        
        Criteria criteria = getCriteria(new Criteria(), condition);
        Query query = Query.query(criteria);
        Update update = new Update();
        
        for (String key : updateMap.keySet()) {
        	Object v = updateMap.get(key);
        	if(Objects.isNull(v)){
        		continue;
        	}
            update.set(key, updateMap.get(key));
        }
        if (type == 1) {
            UpdateResult updateFirst = mongoTemplate.updateFirst(query, update, collName);
            updateFirst.wasAcknowledged();
            return updateFirst.getModifiedCount();
        } else {
            UpdateResult updateMulti = mongoTemplate.updateMulti(query, update, collName);
            return updateMulti.getModifiedCount();
        }
    }
    
	public static Long updateMulti(Map<String, Object> condition, Map<String, Object> updateMap, String collName) {
		return updateMulti(condition, updateMap, collName, 0);
	}
    
    /**
     * 分页查询
     *
     * @param clazz     数据实体类
     * @param collName  集合名称
     * @param map       Map<"查询条件key"，查询条件值> map 若 keys/values 为null,则查询集合中所有数据
     * @param pageNo    当前页
     * @param pageSize  当前页数据条数
     * @param direction Direction.Desc/ASC 排序方式
     * @param sortField 排序字段
     * @return
     */
    public static Map<String,Object> findSortPageCondition(Class<?> clazz, String collName, Map<String, Object> map,
                                                  int pageNo, int pageSize, Sort.Direction direction, String sortField) {
 
        Criteria criteria = getCriteria(new Criteria(), map);
        long count;
 
        if (criteria == null) {
            count = mongoTemplate.count(new Query(), clazz, collName);
        } else {
            count = mongoTemplate.count(new Query(criteria), clazz, collName);
        }
        int pages = (int) Math.ceil((double) count / (double) pageSize);
        if (pageNo <= 0 || pageNo > pages) {
            pageNo = 1;
        }
        int skip = pageSize * (pageNo - 1);
        Query query = new Query().skip(skip).limit(pageSize);
        query.with(Sort.by(direction, sortField));
        if (criteria != null) {
            query.addCriteria(criteria);
        }
        List<?> list = mongoTemplate.find(query, clazz, collName);
        Map<String,Object> page = new HashMap<>();
        page.put("pageNo", pageNo);
        page.put("pageSize", pageSize);
        page.put("total",count);
        page.put("pages",pages);
        page.put("list",list);
        return page;
    }
    
    /**
     * 
     * @param clazz 数据实体类
     * @param collName Map<"查询条件key"，查询条件值> map 若 keys/values 为null,则查询集合中所有数据
     * @param criteria 查询条件
     * @param pageNo 当前页
     * @param pageSize 当前页数据条数
     * @param direction Direction.Desc/ASC 排序方式
     * @param sortField 排序字段
     * @return
     */
    public static Map<String,Object> findSortPage(Class<?> clazz, String collName, Criteria criteria,
                                                  int pageNo, int pageSize, Sort.Direction direction, String sortField) {
        long count;
        if (criteria == null) {
            count = mongoTemplate.count(new Query(), clazz, collName);
        } else {
            count = mongoTemplate.count(new Query(criteria), clazz, collName);
        }
        int pages = (int) Math.ceil((double) count / (double) pageSize);
        if (pageNo <= 0 || pageNo > pages) {
            pageNo = 1;
        }
        int skip = pageSize * (pageNo - 1);
        Query query = new Query().skip(skip).limit(pageSize);
        query.with(Sort.by(direction, sortField));
        if (criteria != null) {
            query.addCriteria(criteria);
        }
        List<?> list = mongoTemplate.find(query, clazz, collName);
        Map<String,Object> page = new HashMap<>();
        page.put("pageNo", pageNo);
        page.put("pageSize", pageSize);
        page.put("total",count);
        page.put("pages",pages);
        page.put("list",list);
        return page;
    }
    
    
    
    
    private static <T> T saveData(String collName, T entiy) {
        if (StringUtils.isEmpty(collName)) {
           return mongoTemplate.save(entiy);
        } else {
            return mongoTemplate.save(entiy, collName);
        }
    }
    
    /**
     * 拼接查询条件,去掉空值筛选
     * @param criteria
     * @param map
     * @return
     */
    public static Criteria getCriteria(Criteria criteria, Map<String, Object> map) {
        if (map == null) {
            return null;
        }
        int i = 0;
        for (String key : map.keySet()) {
        	//不更新空值
        	Object v = map.get(key);
        	if(Objects.isNull(v)){
        		continue;
        	}
        	
        	//字符串空值也不处理
        	if(v instanceof String){
        		String s = String.valueOf(v);
        		if(StringUtils.isBlank(s)){
        			continue;
        		}
        	}
        	
            if (i == 0) {
                criteria = Criteria.where(key).is(v);
                i++;
            } else {
                criteria.and(key).is(v);
            }
        }
        return criteria;
    }
    
    /**
     * 将date转成ISODate 以便mongo识别
     * @param dateStr
     * @return
     */
    public static Date dateToISODate(Date date){
    	Date parse = null;
        try {
            // 解析字符串时间
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            parse = format.parse(format.format(date));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return parse;
    }
    
    /**
     * 集合拼接平台名称
     * @param name
     * @param platFormCode
     * @return
     */
    public static String getCollectionName(String name, String platFormCode){
    	return name + "_" + platFormCode;
    }
    
    /**
     * 集合拼接平台名称
     * @param name
     * @param platFormCode
     * @return
     */
    public static String getCollectionName(Class<?> entityClass, String platFormCode){
    	return mongoTemplate.getCollectionName(entityClass) + "_" + platFormCode;
    }

}
